<?php
/************************************************************************************************
 * Copyright (c) 2004-2008, codesign
 * 
 * All rights reserved.
 * 
 * LICENCE CC/BSD
 * You are free:
 * 
 * to Share & to copy, distribute and transmit the work
 * to Remix & to adapt the work
 * 
 * Under the following conditions:
 * 
 * Attribution. You must attribute the work in the manner specified by the author or 
 * licensor (but not in any way that suggests that they endorse you or your use of the work).
 * Share Alike. If you alter, transform, or build upon this work, you may distribute 
 * the resulting work only under the same, similar or a compatible license.
 * For any reuse or distribution, you must make clear to others the license terms of 
 * this work. The best way to do this is with a link to this web page.
 * Any of the above conditions can be waived if you get permission from the copyright holder.
 * Nothing in this license impairs or restricts the author's moral rights.
 * 
 * LICENCE BSD
 * Redistribution and use in source and binary forms, with or without modification, 
 * are permitted provided that the following conditions are met:
 * 
 * Redistributions of source code must retain the above copyright notice,
 * this list of conditions and the following disclaimer.
 * Redistributions in binary form must reproduce the above copyright notice, 
 * this list of conditions and the following disclaimer in the documentation 
 * and/or other materials provided with the distribution.
 * Neither the name of "codesign" nor the names of its contributors may be used 
 * to endorse or promote products derived from this software without specific prior written permission.
 * 
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
 * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
 * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
 * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 ************************************************************************************************/
if( !defined( 'FLUID_CSS_PARSER' ) ): define( 'FLUID_CSS_PARSER', 1 );

class FluidCssParser
	{
	////////////////////////////////////////////////////////////////////////
	private $source_css_string;
	////////////////////////////////////////////////////////////////////////
	private $parsed_css_string;
	
	////////////////////////////////////////////////////////////////////////
	public function loadString( $css_string )
		{
		$this->source_css_string = $css_string;
		}
		
	////////////////////////////////////////////////////////////////////////
	public static function loadAndParseFile( $filename )
		{
		if( !file_exists( $filename ) ) throw new Exception( 'File "' . $filename . '" does not exist, cannot apply Less CSS !' );
		$parser = new FluidCssParser;
		$parser->loadString( file_get_contents( $filename ) );
		$parser->parse();
		return $parser->getCSS();
		}


	////////////////////////////////////////////////////////////////////////
	public function getCSS()
		{
		return $this->parsed_css_string;
		}

	////////////////////////////////////////////////////////////////////////
	public function parse()
		{
		$this->parsed_css_string = $this->source_css_string;
		$this->parsed_css_string = $this->parseIncludeFiles( $this->parsed_css_string );
		$this->parsed_css_string = $this->parseOperationsOnPixels( $this->parsed_css_string );
		$this->parsed_css_string = $this->parseVariables( $this->parsed_css_string );
		$this->parsed_css_string = $this->parseInclusion( $this->parsed_css_string );
		$this->parsed_css_string = $this->parseOperationsOnPixels( $this->parsed_css_string );
		}

	////////////////////////////////////////////////////////////////////////
	protected function parseIncludeFiles( $css )
		{
		if( preg_match_all( '/\@inc([\s]+)?\(([\s]+)?([a-zA-Z0-9\-\_\/]+)\.css([\s]+)?\);?([[:blank:];[:cntrl:]]+)?/', $css, $matches ) != false )
			{
			for( $index = 0, $end = count( $matches[0] ); $index < $end; $index++ )
				{
				//$content_to_copy = self::loadAndParseFile( FILE_ROOT . $matches[3][$index] . '.css' );
				$content_to_copy = file_get_contents( FILE_ROOT . $matches[3][$index] . '.css' );
				$css = str_replace( $matches[0][$index], $content_to_copy, $css );
				}
			}
		return $css;
		}
		
	////////////////////////////////////////////////////////////////////////
	protected function parseVariables( $css )
		{
		if( preg_match_all( '/\@([a-zA-Z0-9\-\_]+)([\s]+)?:([\s]+)?([[:alnum:]\#\.\_\-\,\"\s]+)/', $css, $matches ) != false )
			{
			for( $index = 0, $end = count( $matches[0] ); $index < $end; $index++ )
				{
				$css = $this->cleanLineWith( $matches[0][$index], $css );
				$css = $this->replaceVariable( $varname = $matches[1][$index], $varvalue = $matches[4][$index], $css );
				}
			}
		return $css;
		}
		
	////////////////////////////////////////////////////////////////////////
	protected function parseInclusion( $css )
		{
		if( preg_match_all( '/\@inc([\s]+)?\(([\s]+)?\.([a-zA-Z0-9\-\_]+)([\s]+)?\);?([[:blank:];[:cntrl:]]+)?/', $css, $matches ) != false )
			{
			for( $index = 0, $end = count( $matches[0] ); $index < $end; $index++ )
				{
				$content_to_copy = $this->getClassContent( $matches[3][$index], $css );
				$css = str_replace( $matches[0][$index], $content_to_copy, $css );
				$css = $this->cleanLineWith( $matches[0][$index], $css );
				}
			}
		return $css;
		}
		
	////////////////////////////////////////////////////////////////////////
	protected function parseOperationsOnPixels( $css )
		{
		$regexp  = "\@eval";
		$regexp .= "([\s]+)?\(([\s]+)?";
		$regexp .= "([\(\)pxem0-9\+\-\*\/\s]+)";
		$regexp .= "([\s]+)?\)([\s]+)?";
		if( preg_match_all( '/'.$regexp.'/', $css, $matches ) != false )
			{
			for( $index = 0, $end = count( $matches[0] ); $index < $end; $index++ )
				{
				$without_operators = strip_tags( preg_replace( '/([a-zA-Z]+)/', '', $matches[3][ $index ] ) );
				eval( "\$result = floor({$without_operators});" );
				//$result .= $this->retrieveUnits( $matches[3][ $index ] );
				$css = str_replace( $matches[0][$index], $result, $css );
				}
			}
		return $css;
		}
		
	////////////////////////////////////////////////////////////////////////
	private function retrieveUnits( $string )
		{
		if(eregi( 'em', $string ) )
			return 'em';
		return 'px';
		}

	////////////////////////////////////////////////////////////////////////
	private function replaceVariable( $varname, $varvalue, $css )
		{
		return str_replace( '@' . $varname, $varvalue, $css );
		}

	////////////////////////////////////////////////////////////////////////
	private function cleanLineWith( $text, $css )
		{
		$regexp = '/([[:blank:];[:cntrl:]]+)?'.$text.'([[:blank:];[:cntrl:]]+)?/s';
		return preg_replace( $regexp, '', $css );
		}

	////////////////////////////////////////////////////////////////////////
	public function getClassContent( $class_name, $css )
		{
		if( preg_match( '/\.' . $class_name . '([\s]+)?\{([\s]+)?([^}]+)\}/', $css, $matches ) )
			{
			return trim( $matches[3] );
			}
		return '';
		}
	}
	
endif;
?>